/*!
 * Fancytree Taxonomy Browser
 *
 * Copyright (c) 2015, Martin Wendt (https://wwWendt.de)
 *
 * Released under the MIT license
 * https://github.com/mar10/fancytree/wiki/LicenseInfo
 *
 * @version @VERSION
 * @date @DATE
 */

/* global Handlebars */
/* eslint-disable no-console */

(function($, window, document) {
	"use strict";

	/*******************************************************************************
	 * Private functions and variables
	 */

	var taxonTree,
		searchResultTree,
		timerMap = {},
		tmplDetails, // =
		USER_AGENT = "Fancytree Taxonomy Browser/1.0",
		ITIS_URL = "//www.itis.gov/ITISWebService/jsonservice/",
		glyphOpts = {
			preset: "bootstrap3",
			map: {
				expanderClosed: "glyphicon glyphicon-menu-right", // glyphicon-plus-sign
				expanderLazy: "glyphicon glyphicon-menu-right", // glyphicon-plus-sign
				expanderOpen: "glyphicon glyphicon-menu-down", // glyphicon-collapse-down
			},
		};

	// Load and compile handlebar templates

	$.get("details.tmpl", function(data) {
		tmplDetails = Handlebars.compile(data);
	});

	/** Update UI elements according to current status
	 */
	function updateControls() {
		var query = $.trim($("input[name=query]").val());

		$("#btnPin").attr("disabled", !taxonTree.getActiveNode());
		$("#btnUnpin")
			.attr("disabled", !taxonTree.isFilterActive())
			.toggleClass("btn-success", taxonTree.isFilterActive());
		$("#btnResetSearch").attr("disabled", query.length === 0);
		$("#btnSearch").attr("disabled", query.length < 2);
	}

	/**
	 * Invoke callback after `ms` milliseconds.
	 * Any pending action of this type is cancelled before.
	 */
	function _delay(tag, ms, callback) {
		/*jshint -W040:true */
		var self = this;

		tag = "" + (tag || "default");
		if (timerMap[tag] != null) {
			clearTimeout(timerMap[tag]);
			delete timerMap[tag];
			// console.log("Cancel timer '" + tag + "'");
		}
		if (ms == null || callback == null) {
			return;
		}
		// console.log("Start timer '" + tag + "'");
		timerMap[tag] = setTimeout(function() {
			// console.log("Execute timer '" + tag + "'");
			callback.call(self);
		}, +ms);
	}

	/**
	 */
	function _callItis(cmd, data) {
		return $.ajax({
			url: ITIS_URL + cmd,
			data: $.extend(
				{
					jsonp: "itis_data",
				},
				data
			),
			cache: true,
			headers: { "Api-User-Agent": USER_AGENT },
			jsonpCallback: "itis_data",
			dataType: "jsonp",
		});
	}

	/**
	 */
	// function countMatches(query) {
	// 	$("#tsnDetails").text("Loading TSN " + tsn + "...");
	// 	_callItis("getAnyMatchCount", {
	// 		srchKey: query
	// 	}).done(function(result){
	// 		console.log("updateTsnDetails", result);
	// 		$("#tsnDetails").html(tmplDetails(result));
	// 		updateControls();
	// 	});
	// }

	/**
	 */
	function updateTsnDetails(tsn) {
		$("#tsnDetails").addClass("busy");
		// $("#tsnDetails").text("Loading TSN " + tsn + "...");
		$.bbq.pushState({ tsn: tsn });

		_callItis("getFullRecordFromTSN", {
			tsn: tsn,
		}).done(function(result) {
			console.log("updateTsnDetails", result);
			$("#tsnDetails")
				.html(tmplDetails(result))
				.removeClass("busy");

			updateControls();
		});
	}

	/**
	 */
	function updateBreadcrumb(tsn, loadTreeNodes) {
		// var $ol = $("ol.breadcrumb").text("...");
		var $ol = $("ol.breadcrumb").addClass("busy");
		_callItis("getFullHierarchyFromTSN", {
			tsn: tsn,
		}).done(function(result) {
			console.log("updateBreadcrumb", result);
			// Convert to simpler format
			var list = [];
			// Display as <OL> list (for Bootstrap breadcrumbs)
			$ol.empty().removeClass("busy");
			$.each(result.hierarchyList, function(i, o) {
				if (o.parentTsn === tsn) {
					return;
				} // skip direct children
				list.push(o.tsn);
				if (o.tsn === tsn) {
					$ol.append(
						$("<li class='active'>").append(
							$("<span>", {
								text: o.taxonName,
								title: o.rankName,
							})
						)
					);
				} else {
					$ol.append(
						$("<li>").append(
							$("<a>", {
								href: "#tsn=" + o.tsn,
								text: o.taxonName,
								title: o.rankName,
							})
						)
					);
				}
			});
			if (loadTreeNodes) {
				console.log("updateBreadcrumb - loadKeyPath", list);
				taxonTree.loadKeyPath("/" + list.join("/"), function(
					node,
					status
				) {
					// console.log("... updateBreadcrumb - loadKeyPath", status, node);
					switch (status) {
						case "loaded":
							node.makeVisible();
							break;
						case "ok":
							node.setActive();
							break;
					}
				});
			}
		});
	}

	/**
	 */
	function search(query) {
		query = $.trim(query);
		console.log("searching for '" + query + "'...");
		// NOTE:
		// It seems that ITIS searches don't work with jsonp (always return valid
		// but empty result sets).
		// When debugging, make sure cross domain requests are allowed.
		searchResultTree
			.reload({
				url: ITIS_URL + "searchForAnyMatchPaged",
				data: {
					// jsonp: "itis_data",
					srchKey: query,
					pageSize: 10,
					pageNum: 1,
					ascend: false,
				},
				cache: true,
				// jsonpCallback: "itis_data",
				// dataType: "jsonp"
			})
			.done(function(result) {
				// console.log("search returned", result);
				// result.anyMatchList
				updateControls();
			});
	}

	/*******************************************************************************
	 * Pageload Handler
	 */

	$(function() {
		$("#taxonTree").fancytree({
			extensions: ["filter", "glyph", "wide"],
			filter: {
				mode: "hide",
			},
			glyph: glyphOpts,
			activeVisible: true,
			source: {
				// We could use getKingdomNames, but that returns an individual JSON format.
				// getHierarchyDownFromTSN?tsn=0 seems to work as well and allows
				// unified parsing in postProcess.
				// url: ITIS_URL + "getKingdomNames",
				url: ITIS_URL + "getHierarchyDownFromTSN",
				data: {
					jsonp: "itis_data",
					tsn: "0",
				},
				cache: true,
				jsonpCallback: "itis_data",
				dataType: "jsonp",
			},
			init: function(event, data) {
				updateControls();
				$(window).trigger("hashchange"); // trigger on initial page load
			},
			lazyLoad: function(event, data) {
				data.result = {
					url: ITIS_URL + "getHierarchyDownFromTSN",
					data: {
						jsonp: "itis_data",
						tsn: data.node.key,
					},
					cache: true,
					jsonpCallback: "itis_data",
					dataType: "jsonp",
				};
			},
			postProcess: function(event, data) {
				var response = data.response;

				data.node.info(response);
				data.result = $.map(response.hierarchyList, function(o) {
					return (
						o && {
							title: o.taxonName,
							key: o.tsn,
							folder: true,
							lazy: true,
						}
					);
				});
			},
			activate: function(event, data) {
				$("#tsnDetails").addClass("busy"); //text("...");
				updateControls();
				_delay("showDetails", 1000, function() {
					updateTsnDetails(data.node.key);
					updateBreadcrumb(data.node.key);
				});
			},
		});

		$("#searchResultTree").fancytree({
			extensions: ["table", "wide"],
			source: [{ title: "No Results." }],
			minExpandLevel: 2,
			icon: false,
			table: {
				nodeColumnIdx: 1,
			},
			postProcess: function(event, data) {
				var response = data.response;

				data.node.info("pp", response);
				data.result = $.map(response.anyMatchList, function(o) {
					if (!o) {
						return;
					}
					var res = {
						title: o.sciName,
						key: o.tsn,
						author: o.author,
						matchType: o.matchType,
					};
					res.commonNames = $.map(
						o.commonNameList.commonNames,
						function(x) {
							return x && x.commonName
								? { name: x.commonName, language: x.language }
								: undefined;
						}
					);
					return res;
				});
				// console.log("pp2", data.result)
			},
			renderColumns: function(event, data) {
				var node = data.node,
					$tdList = $(node.tr).find(">td"),
					cnList = node.data.commonNames
						? $.map(node.data.commonNames, function(o) {
								return o.name;
						  })
						: [];

				$tdList.eq(0).text(node.key);
				$tdList.eq(2).text(cnList.join(", "));
				$tdList.eq(3).text(node.data.matchType);
				$tdList.eq(4).text(node.data.author);
			},
			activate: function(event, data) {
				_delay("activateNode", 1000, function() {
					updateTsnDetails(data.node.key);
					updateBreadcrumb(data.node.key);
				});
			},
		});

		taxonTree = $.ui.fancytree.getTree("#taxonTree");
		searchResultTree = $.ui.fancytree.getTree("#searchResultTree");

		// Bind a callback that executes when document.location.hash changes.
		// (This code uses bbq: https://github.com/cowboy/jquery-bbq)
		$(window).on("hashchange", function(e) {
			var tsn = $.bbq.getState("tsn");
			console.log("bbq tsn", tsn);
			if (tsn) {
				updateBreadcrumb(tsn, true);
			}
		}); // don't trigger now, since we need the the taxonTree root nodes to be loaded first

		$("input[name=query]")
			.on("keyup", function(e) {
				var query = $.trim($(this).val());

				if ((e && e.which === $.ui.keyCode.ESCAPE) || query === "") {
					$("#btnResetSearch").click();
					return;
				}
				if (e && e.which === $.ui.keyCode.ENTER && query.length >= 2) {
					$("#btnSearch").click();
					return;
				}
				$("#btnResetSearch").attr("disabled", query.length === 0);
				$("#btnSearch").attr("disabled", query.length < 2);
			})
			.focus();

		$("#btnResetSearch").click(function(e) {
			$("#searchResultPane").collapse("hide");
			$("input[name=query]").val("");
			searchResultTree.clear();
			// $("#btnSearch").attr("disabled", true);
			// $(this).attr("disabled", true);
			updateControls();
		});

		$("#btnSearch")
			.click(function(event) {
				$("#searchResultPane").collapse("show");
				search($("input[name=query]").val());
			})
			.attr("disabled", true);

		$("#btnPin").click(function(event) {
			taxonTree.filterBranches(function(n) {
				return n.isActive();
			});
			updateControls();
		});
		$("#btnUnpin").click(function(event) {
			taxonTree.clearFilter();
			updateControls();
		});

		// -----------------------------------------------------------------------------
	}); // end of pageload handler
})(jQuery, window, document);
